#include "vcd-send-heartbeat.h"
#include "vcd-log.h"
#include "vcd-util.h"
#include "vcd-conf/vcd-conf.h"
#include "vcd-download.h"
#include "vcd-common.h"
#include "../vcd-linux-ui/vcd-setting-window/setting-network/vcd-wire.h"
#include "../vcd-linux-ui/vcd-setting-window/setting-main/vcd-sys-info.h"


SendHeartBeat *g_heartbeat_data = NULL;
static volatile gboolean heartbeat_thread_flag = TRUE;

static int create_heartbeat_context(HbData *hb_data);
static void stop_heartbeat_thread();
static void free_heartbeat_context();
static int start_send_heartbeat(HbData *hb_data, const char *gateway_ip, const char *gateway_port);


static gboolean tell_gateway_client_disconnect_func(gpointer data)
{
    g_return_val_if_fail(data != NULL, FALSE);
    network_response *res = (network_response*)data;
    if (res->memory == NULL) {
        LOG_ERROR("res->memory == NULL");
        goto fail;
    }
fail:
    network_response_free(&res);
    return FALSE;
}

void tell_gateway_client_disconnect()
{
    if(!g_heartbeat_data)
        return;

    network_request_arg *req = (network_request_arg*)malloc(sizeof(network_request_arg));
    g_return_if_fail(req != NULL);

    snprintf(req->post_fields, sizeof(req->post_fields),
    "{\"vmid\":\"%s\", \"name\":\"%s\", \"node\":\"%s\", \"protocol\":\"%s\", "
    "\"username\":\"%s\", \"instance_id\":\"%s\", \"local_ip\":\"%s\", \"platform\":\"%s\"}",
                           g_heartbeat_data->hb_data.vmid,
                           g_heartbeat_data->hb_data.vmname,
                           g_heartbeat_data->hb_data.vmnode,
                           g_heartbeat_data->hb_data.protocol,
                           g_heartbeat_data->hb_data.username,
                           g_heartbeat_data->hb_data.instance_id,
                           g_heartbeat_data->local_ip,
                           g_heartbeat_data->platform);
    snprintf(req->url, sizeof(req->url), "https://%s:%s/client/disconnect",
                                         g_heartbeat_data->host_ip,
                                         g_heartbeat_data->port);
    req->http_method = CURLOPT_POST;
    req->timeout = 3;
    req->list = NULL;
    req->data_priv = NULL;
    req->callback = tell_gateway_client_disconnect_func;

    net_req_list_add(req);
    M_DEBUG("tell gateway client disconnect");

    destroy_heartbeat();
}

static int create_heartbeat_context(HbData *hb_data)
{
    heartbeat_thread_flag = TRUE;
    SendHeartBeat* shb = (SendHeartBeat*)g_malloc0(sizeof(SendHeartBeat));
    g_return_val_if_fail(shb != NULL, -1);

    shb->hb_data.type = hb_data->type;
    if (hb_data->type == CONN_HEARTBEAT) {
        strncpy(shb->hb_data.vmid, hb_data->vmid, sizeof(hb_data->vmid));
        strncpy(shb->hb_data.vmnode, hb_data->vmnode, sizeof(hb_data->vmnode));
        strncpy(shb->hb_data.vmname, hb_data->vmname, sizeof(hb_data->vmname));
        strncpy(shb->hb_data.username, hb_data->username, sizeof(hb_data->username));
        strncpy(shb->hb_data.protocol, hb_data->protocol, sizeof(hb_data->protocol));
        strncpy(shb->hb_data.instance_id, hb_data->instance_id, sizeof(hb_data->instance_id));
    } else {
        strncpy(shb->hb_data.client_version, hb_data->client_version, sizeof(hb_data->client_version));
        strncpy(shb->hb_data.pve_id, hb_data->pve_id, sizeof(hb_data->pve_id));
        strncpy(shb->hb_data.pve_version, hb_data->pve_version, sizeof(hb_data->pve_version));
        strncpy(shb->hb_data.hostname, hb_data->hostname, sizeof(hb_data->hostname));
        strncpy(shb->hb_data.pve_full_token, hb_data->pve_full_token, sizeof(hb_data->pve_full_token));
        strncpy(shb->hb_data.pve_wsagent_token, hb_data->pve_wsagent_token, sizeof(hb_data->pve_wsagent_token));
    }

    if (get_local_ip(shb->local_ip) == -1) {
        LOG_ERROR("get local ip error");
        sprintf(shb->local_ip, "none");
    }

#if defined (__WIN32__)
    sprintf(shb->platform, "pc");
#else
    sprintf(shb->platform, "thin-client");
#endif

    GMutex* thd_mutex = g_malloc0(sizeof(GMutex));
    if (thd_mutex == NULL) {
        LOG_ERROR("new mutex failed");
        goto fail;
    }
    g_mutex_init(thd_mutex);

    GCond* thd_cond = g_malloc0(sizeof(GCond));
    if (thd_cond == NULL){
        LOG_ERROR("new cond failed");
        goto fail;
    }
    g_cond_init(thd_cond);

    shb->thd_cond = thd_cond;
    shb->thd_mutex = thd_mutex;

    g_heartbeat_data = shb;
    return 0;
fail:
    if (thd_mutex) {
        g_mutex_clear(thd_mutex);
        thd_mutex = NULL;
    }
    if(thd_cond) {
        g_cond_clear(thd_cond);
        thd_cond = NULL;
    }
    G_SAFE_FREE(shb);
    return -1;
}

void stop_heartbeat_thread()
{
    heartbeat_thread_flag = FALSE;

    //wait thread exit
    g_thread_join(g_heartbeat_data->heartbeat_thread);
    g_heartbeat_data->heartbeat_thread = NULL;
}

void free_heartbeat_context()
{
    if (g_heartbeat_data->heartbeat_thread) {
        g_thread_unref(g_heartbeat_data->heartbeat_thread);
        g_heartbeat_data->heartbeat_thread = NULL;
    }

    if (g_heartbeat_data->thd_mutex) {
        g_mutex_clear(g_heartbeat_data->thd_mutex);
        g_heartbeat_data->thd_mutex = NULL;
    }

    if (g_heartbeat_data->thd_cond) {
        g_cond_clear(g_heartbeat_data->thd_cond);
        g_heartbeat_data->thd_cond = NULL;
    }
    g_free(g_heartbeat_data);
    g_heartbeat_data = NULL;
}

void destroy_heartbeat()
{
    M_DEBUG("destroy_heartbeat");

    if (g_heartbeat_data == NULL)
        return;

    stop_heartbeat_thread();
    free_heartbeat_context();
}

static gboolean heartbeat_response_callback(gpointer data)
{
    struct network_response* res = (struct network_response*)data;

    if (res == NULL)
        goto fail;

    char *memory = res->memory;
    if (!memory) {
        //LOG_ERROR("memory = NULL");
        goto fail;
    }

    if (res->error_msg != NULL) {
        LOG_ERROR("curl get error message = %s", res->error_msg);
        goto fail;
    }

fail:
    network_response_free(&res);
    return FALSE;
}

static gboolean localmode_heartbeat_response_callback(gpointer data)
{
    struct network_response* res = (struct network_response*)data;

    if (res == NULL)
        goto fail;

    char *memory = res->memory;
    if (!memory) {
        //LOG_ERROR("memory = NULL");
        goto fail;
    }

    if (res->error_msg != NULL) {
        LOG_ERROR("curl get error message = %s", res->error_msg);
        goto fail;
    }

    //要将用户列表传进去存储
    int count = 0;
    int ret = 0;
    char tmp_error_msg[1024] = {0};
    ret = json_parser_heartbeat_record_user_pass_config(memory, &count, tmp_error_msg);
    if (ret != 0) {
        //message_box(tmp_error_msg);
        goto fail;
    }

fail:
    network_response_free(&res);
    return FALSE;
}

#define SLEEPTIME 500000//0.5s
#define SLEEPTIMENUM 10 //10x0.5=5s

static void send_heartbeat_func()
{
    int n = 0;

    while (heartbeat_thread_flag) {

        struct curl_slist *list = NULL;

        g_usleep(500000);
        if (++n < 10) {
            continue;
        } else {
            n = 0;
        }

        struct network_request_arg *req = g_malloc0(sizeof(struct network_request_arg));
        if (req == NULL) {
            LOG_ERROR("malloc network_req_arg error");
            break;
        }

        g_mutex_lock(g_heartbeat_data->thd_mutex);

        if (g_heartbeat_data->hb_data.type == CONN_HEARTBEAT) {
            snprintf(req->post_fields, sizeof(req->post_fields),
            "{\"vmid\":\"%s\", \"name\":\"%s\", \"node\":\"%s\", \"protocol\":\"%s\", "
            "\"username\":\"%s\", \"instance_id\":\"%s\", \"local_ip\":\"%s\", \"platform\":\"%s\"}",
                                   g_heartbeat_data->hb_data.vmid,
                                   g_heartbeat_data->hb_data.vmname,
                                   g_heartbeat_data->hb_data.vmnode,
                                   g_heartbeat_data->hb_data.protocol,
                                   g_heartbeat_data->hb_data.username,
                                   g_heartbeat_data->hb_data.instance_id,
                                   g_heartbeat_data->local_ip,
                                   g_heartbeat_data->platform);
            snprintf(req->url, sizeof(req->url), "https://%s:%s/client/heartbeat",
                                        g_heartbeat_data->host_ip,
                                        g_heartbeat_data->port);
            req->list = NULL;
        } else {
            snprintf(req->post_fields, sizeof(req->post_fields),
            "{\"clinet_version\":\"%s\", \"machine_id\":\"%s\", \"pve_version\":\"%s\", "
            "\"name\":\"%s\", \"user_id\":\"%s\", \"token\":\"%s\", \"mip\":\"%s\"}",
                                   g_heartbeat_data->hb_data.client_version,
                                   g_heartbeat_data->hb_data.pve_id,
                                   g_heartbeat_data->hb_data.pve_version,
                                   g_heartbeat_data->hb_data.hostname,
                                   g_heartbeat_data->hb_data.pve_full_token,
                                   g_heartbeat_data->hb_data.pve_wsagent_token,
                                   g_heartbeat_data->local_ip);
            snprintf(req->url, sizeof(req->url), "https://%s/API/workspace/terminal/register",
                                        g_heartbeat_data->host_ip);
            list = curl_slist_append(list, "Content-Type: application/json; charset=utf-8");
            req->list = list;
        }


        g_mutex_unlock(g_heartbeat_data->thd_mutex);

        M_DEBUG("send heartbeat [url = %s, post data = %s]", req->url, req->post_fields);

        req->timeout = 5L;
        req->data_priv = NULL;
        req->http_method = CURLOPT_POST;

        if (g_heartbeat_data->hb_data.type == CONN_HEARTBEAT)
            req->callback = heartbeat_response_callback;
        else
            req->callback = localmode_heartbeat_response_callback;

        net_req_list_add(req);
    }
}

static int start_send_heartbeat(HbData *hb_data, const char *host_ip, const char *port)
{
    if (g_heartbeat_data)
        destroy_heartbeat();

    int ret = create_heartbeat_context(hb_data);
    if (-1 == ret) {
        LOG_ERROR("create heartbeat data error");
        return -1;
    }
    sprintf(g_heartbeat_data->host_ip, "%s", host_ip);
    sprintf(g_heartbeat_data->port, "%s", port);

    GThread * thd = NULL;
    thd = g_thread_new("vcd_heartbeat", (GThreadFunc)send_heartbeat_func, NULL);
    if (thd == NULL) {
        destroy_heartbeat();
        LOG_ERROR("create heartbeat thread error");
        return -1;
    }
    g_heartbeat_data->heartbeat_thread = thd;

    return 0;
}

void start_heartbeat(spice_connect_data* scd) {

    M_DEBUG("start gateway heartbeat");

    HbData data = {0};
    data.type = CONN_HEARTBEAT;
    sprintf(data.vmid, "%d", scd->vmid);
    sprintf(data.vmnode, "%s", scd->vmnode);
    sprintf(data.vmname, "%s", scd->vmname);
    sprintf(data.protocol, "%s", scd->protocol);
    sprintf(data.username, "%s", scd->username);
    sprintf(data.instance_id, "%s", scd->instance_id);

    start_send_heartbeat(&data, scd->rdp_host, scd->api_port);
}

void start_localmode_heartbeat() {

    M_DEBUG("start localmode heartbeat");

    HbData data = {0};
    data.type = LOCALMODE_HEARTBEAT;
    sprintf(data.client_version, "%s", PACKAGE_VERSION ? PACKAGE_VERSION : "");
    
    char *pve_id = get_pve_info("dmidecode -s system-uuid", 50);
    sprintf(data.pve_id, "%s", pve_id ? pve_id : "");

    char *pve_version = get_pve_info("pveversion |awk '{print $1}'|cut -d / -f2", 50);
    sprintf(data.pve_version, "%s", pve_version ? pve_version : "");
    
    char *hostname = get_pve_info("uname -a |awk '{print $2}'", 50);
    sprintf(data.hostname, "%s", hostname ? hostname : "");

    char *pve_full_token = get_pve_info("cat /etc/pve/priv/token.cfg|grep wsagent|awk '{print $1}'", 50);
    sprintf(data.pve_full_token, "%s", pve_full_token ? pve_full_token : "");
    
    char *pve_wsagent_token = get_pve_info("cat /etc/pve/priv/token.cfg|grep wsagent|awk '{print $2}'", 50);
    sprintf(data.pve_wsagent_token, "%s", pve_wsagent_token ? pve_wsagent_token : "");

    SAFE_FREE(pve_id);
    SAFE_FREE(hostname);
    SAFE_FREE(pve_version);
    SAFE_FREE(pve_full_token);
    SAFE_FREE(pve_wsagent_token);

    start_send_heartbeat(&data, g_vcd_conf->ip, "");
}

void change_heartbeat_host(const char* host)
{
    if (g_heartbeat_data) {
        g_mutex_lock(g_heartbeat_data->thd_mutex);
        memset(g_heartbeat_data->host_ip, 0, 50);
        strncpy(g_heartbeat_data->host_ip, host, 50);
        g_mutex_unlock(g_heartbeat_data->thd_mutex);
    }
}